package com.wb.spring.boot.autoconfigure;

import com.wb.spring.boot.autoconfigure.listener.WbRegisterListener;
import com.wb.spring.boot.autoconfigure.listener.WbUnRegisterListener;
import com.wb.spring.boot.autoconfigure.load.Load;
import com.wb.spring.boot.autoconfigure.load.PollingLoad;
import com.wb.spring.boot.autoconfigure.model.ServerInfo;
import com.wb.spring.boot.autoconfigure.model.WbProperties;
import com.wb.spring.boot.autoconfigure.proxy.WbClientCall;
import com.wb.spring.boot.autoconfigure.register.RedisRegister;
import com.wb.spring.boot.autoconfigure.register.RedisSubscriber;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.web.client.RestTemplate;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.Duration;
import java.util.Collections;
import java.util.Set;


/**
 * @Author wenbin.xu
 * @Date 2022/8/25 19:25
 */
@ConditionalOnProperty(
        prefix = "wb",
        name="enabled",
        havingValue = "true"
)
@EnableConfigurationProperties({WbProperties.class})
@Configuration
public class WbRpcAutoConfiguration {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        return builder
                //设置连接超时时间
                .setConnectTimeout(Duration.ofSeconds(5000))
                //设置读取超时时间
                .setReadTimeout(Duration.ofSeconds(5000))
                //构建
                .build();
    }

    @Bean
    public MyBeanDefinitionRegistryPostProcessor myBeanDefinitionRegistryPostProcessor(Environment environment) {
        String packagesScan = environment.getProperty("wb.packageScan", String.class, null);
        return new MyBeanDefinitionRegistryPostProcessor(packagesScan);
    }


    /**
     * redis连接
     * @param wbProperties
     * @return
     */
    @ConditionalOnProperty(
            prefix = "wb",
            name="register-type",
            havingValue = "redis"
    )
    @Bean
    public JedisConnectionFactory jedisConnectionFactory(WbProperties wbProperties) {
        WbProperties.RedisRegister redisRegister = wbProperties.getRedisRegister();
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration(redisRegister.getHost(), redisRegister.getPort());
        if (StringUtils.isNotBlank(redisRegister.getPassword())) {
            config.setPassword(redisRegister.getPassword());
        }

        return new JedisConnectionFactory(config);
    }

    /**
     * 注册器
     * @param environment
     *   环境变量
     * @param jedisConnectionFactory
     *  redis连接池
     * @param load
     *  负载均衡
     * @return
     * @throws UnknownHostException
     */
    @ConditionalOnBean(JedisConnectionFactory.class)
    @Bean
    public RedisRegister redisRegister(Environment environment, JedisConnectionFactory jedisConnectionFactory, Load load) throws UnknownHostException {
        // 创建JSON序列化工具
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        RedisTemplate redisTemplate = new RedisTemplate();
        // 设置key的序列化
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        // 设置value的序列化
        redisTemplate.setValueSerializer(jsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jsonRedisSerializer);
        redisTemplate.setConnectionFactory(jedisConnectionFactory);
        // 解析处本服务的基本信息
        ServerInfo serverInfo = new ServerInfo(environment);
        return new RedisRegister(redisTemplate, serverInfo, load);
    }

    /**
     * redis监听器
     * @param redisRegister
     *  redis注册中心
     * @param jedisConnectionFactory
     *  redis连接池
     * @return
     */
    @ConditionalOnBean(JedisConnectionFactory.class)
    @Bean
    RedisMessageListenerContainer container(RedisRegister redisRegister, JedisConnectionFactory jedisConnectionFactory) {
        MessageListenerAdapter redisSubscriber = new RedisSubscriber(redisRegister);
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(jedisConnectionFactory);
        container.addMessageListener(redisSubscriber, new PatternTopic("wbrpc"));
        return container;
    }

    /**
     * 服务启动后的监听器
     * @return
     */
    @Bean
    public WbRegisterListener wbRegisterListener() {
        return new WbRegisterListener();
    }

    /**
     * 服务停止的监听器
     * @return
     */
    @Bean
    public WbUnRegisterListener wbUnRegisterListener() {
        return new WbUnRegisterListener();
    }

    @Bean
    public WbClientCall clientCall() {
        return new WbClientCall();
    }

    @ConditionalOnMissingBean(Load.class)
    @Bean
    public Load load() {
        Load load = new PollingLoad();
        return load;
    }
}
